/*
 * Copyright 2012-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package smoketest.tomcat;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.util.stream.Stream;

@EnableAspectJAutoProxy
@SpringBootApplication
public class SampleTomcatApplication {

	private static Log logger = LogFactory.getLog(SampleTomcatApplication.class);

	@Bean
	protected ServletContextListener listener() {
		return new ServletContextListener() {

			@Override
			public void contextInitialized(ServletContextEvent sce) {
				logger.info("ServletContext initialized");
			}

			@Override
			public void contextDestroyed(ServletContextEvent sce) {
				logger.info("ServletContext destroyed");
			}

		};
	}

	public static void main(String[] args) {
		SpringApplication springApplication = new SpringApplication(SampleTomcatApplication.class);
		//springApplication.setWebApplicationType(WebApplicationType.SERVLET); //强制使用WebMvc环境
		//springApplication.setBannerMode(Banner.Mode.OFF); //不打印Banner
		springApplication.run(args);
	}
 /**
  * SpringBoot 应用打包启动的两种方式：
  * 1、打jar包启动时，先创建IOC容器，在创建过程中创建了嵌入式Web容器。（详细的jar包启动会在 JarLauncher 篇解析）
  * 2、打war包启动时，要先启动外部的Web服务器，Web服务器再去启动 SpringBoot 应用，然后才是创建IOC容器。
  *   打war包启动时，里面最核心的步骤：Web服务器启动SpringBoot应用 。而这个步骤，就需要依靠 SpringBootServletInitializer
  *   2.1、外部Web容器（Tomcat、Jetty、Undertow等）启动，开始加载 SpringBoot 的war 包并解压。
  *   2.2、去 SpringBoot 应用中的每一个被依赖的jar中寻找 META-INF/services/javax.servlet.SpringBootServletInitializer 的文件。
  *   2.3、根据文件中标注的全限定类名，去找这个类（就是 SpringServletContainerInitializer）。
  *   2.4、这个类的 onStartup 方法中会将 @HandlesTypes 中标注的类型的所有普通实现类（也就是非抽象子类）都实例化出来，之后分别调他们自己的 onStartup 方法。
  *   2.5、因为打war包的 SpringBoot 工程会在启动类的同包下创建 ServletInitializer ，并且必须继承 SpringBootServletInitializer，所以会被服务器创建对象。
  *   2.6、SpringBootServletInitializer 没有重写 onStartup 方法，去父类 SpringServletContainerInitializer 中寻找，父类 SpringServletContainerInitializer 中的 onStartup方法中有核心源码：
  *         WebApplicationContextrootAppContext rootAppContext = createRootApplicationContext(servletContext);
  *        	2.6.1、先创建 SpringApplicationBuilder 应用构建器；
  * 		2.6.2、再创建一些环境配置；
  * 		2.6.3、下面中间部分有一句： builder = configure(builder)（【引导】多态进入子类（自己定义）的方法中）;
  * 		2.6.4、这句源码由于多态，执行了子类（SpringBoot 工程中必须写的那个启动类的同包下的 ServletInitializer）重写的方法；
  * 		2.6.5、又因为重写的格式固定，是传入了 SpringBoot 的目标运行主程序；
  * 		2.6.6、return builder.sources(DemoApplication.class);
  * 		2.6.7、所以下一步才能启动 SpringBoot 工程。
  *   2.7、之后就跟启动运行主程序 SpringBootApplication 没什么区别了
  *
  *
  * DispatcherServlet工作流程步骤
  * 1、浏览器向服务器发起请求，由 DispatcherServlet 接收请求；
  * 2、DispatcherServlet 委托 HandlerMapping，根据 url 来选择一个合适的 Controller 中的方法；
  * 3、HandlerMapping 找到合适的 Controller 后，并根据已配置的拦截器，整理出一个 Handler，返回给 DispatcherServlet；
  * 4、DispatcherServlet 收到 Handler 后委托 HandlerAdapter，将该请求代理给 HandlerMapping 选定的 Controller 中的 Handler；
  * 5、Handler 收到请求后，实际执行 Controller 中的方法，执行完毕后会返回 ModelAndView；
  * 6、Controller 方法执行完毕后会返回 ModelAndView；
  * 7、HandlerAdapter 收到 Handler 返回的 ModelAndView 后返回给 DispatcherServlet；
  * 8、DispatcherServlet 拿到 ModelAndView 后委托 ViewResolver，由 ViewResolver 负责渲染视图；
  * 9、ViewResolver 渲染视图完成后，返回给 DispatcherServlet，由 DispatcherServlet 负责响应视图。
  * 小结:
  * 1、DispatcherServlet 最终继承了 HttpServlet ，核心方法是 doDispatch 。
  * 2、DispatcherServlet 的核心流程是先获取 HandlerMapping ，后获取 HandlerAdapter ，最终执行目标 Controller 的方法，最终返回 View 或响应 json。
  */
}
